package com.luxsoft.sw3.services;

import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.BeanPropertyValueEqualsPredicate;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import ca.odell.glazedlists.GlazedLists;

import com.luxsoft.siipap.cxc.model.OrigenDeOperacion;
import com.luxsoft.siipap.cxc.rules.RevisionDeCargosRules;
import com.luxsoft.siipap.inventarios.dao.ExistenciaDao;
import com.luxsoft.siipap.inventarios.model.Existencia;
import com.luxsoft.siipap.model.Direccion;
import com.luxsoft.siipap.model.Periodo;
import com.luxsoft.siipap.model.Sucursal;
import com.luxsoft.siipap.model.User;
import com.luxsoft.siipap.model.core.Cliente;
import com.luxsoft.siipap.service.core.ClienteManager;
import com.luxsoft.siipap.ventas.dao.VentaDao;
import com.luxsoft.siipap.ventas.model.Cobrador;
import com.luxsoft.siipap.ventas.model.Vendedor;
import com.luxsoft.siipap.ventas.model.Venta;
import com.luxsoft.siipap.ventas.model.Venta.TipoDeVenta;
import com.luxsoft.siipap.ventas.model.VentaDet;
import com.luxsoft.sw3.cfd.dao.FolioFiscalDao;
import com.luxsoft.sw3.cfd.model.FolioFiscal;
import com.luxsoft.sw3.cfdi.CFDIManager;
import com.luxsoft.sw3.gasoc.model.Factura;
import com.luxsoft.sw3.ventas.Pedido;
import com.luxsoft.sw3.ventas.PedidoDet;
import com.luxsoft.utils.LoggerHelper;

/**
 * Implementacion de VentasManager
 * 
 * @author Ruben Cancino 
 *
 */
@Service("ventasManager")
@Transactional(propagation=Propagation.SUPPORTS,readOnly=true)
public class VentasManagerImpl implements VentasManager{
	
	@Autowired
	private VentaDao ventaDao;
	
	@Autowired
	private FolioFiscalDao folioDao;
	
	@Autowired
	private ExistenciaDao existenciaDao;
	
	@Autowired
	private HibernateTemplate hibernateTemplate;
	
	@Autowired
	private PedidosManager pedidosManager;
	
	@Autowired
	private ClienteManager clienteManager;
	
	@Autowired
	private CFDIManager cfdiManager;
	
	private Logger logger=LoggerHelper.getLogger();

	@Transactional(propagation=Propagation.REQUIRED)
	public Venta getVenta(String ventaId) {
		Venta v=ventaDao.get(ventaId);
		hibernateTemplate.initialize(v.getPedido());
		Hibernate.initialize(v.getCliente());
		Hibernate.initialize(v.getCliente().getTelefonos());
		Hibernate.initialize(v.getPartidas());
		Hibernate.initialize(v.getVendedor());
		Hibernate.initialize(v.getCobrador());
		if(v.getPedido()!=null){
			Hibernate.initialize(v.getPedido());
		}
		Hibernate.initialize(v.getSocio());
		return v;
	}

	@Override
	public Venta cancelarVenta(String id, Date fecha, User usuario,String motivo) {
		
		return null;
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public Venta generarVenta(Pedido pedido) {
		Assert.isTrue(!pedido.isFacturado(),"El pedido ya esta facturado por lo que no es posible actualizar ni modificar");
		if(pedido.getTipoDeVenta().equals(TipoDeVenta.CAFE))
			pedido.setTipo(Pedido.Tipo.CONTADO);
		else
			pedido.setTipo(Pedido.Tipo.CREDITO);
		pedido=this.pedidosManager.save(pedido);
		
		Venta venta=new Venta(pedido);
		venta.setFecha(new Date());
		if(pedido.isDeCredito()){
			RevisionDeCargosRules.instance().actualizar(venta, venta.getFecha());
			//venta.setOrigen(OrigenDeOperacion.CAM);
		}
		venta.setDocumento(0L);
		venta.setNumeroFiscal(0);
		for(PedidoDet det:pedido.getPartidas()){
			VentaDet vdet=det.toVentaDet();
			vdet.setDocumento(venta.getDocumento());
			vdet.setDescuentoOriginal(pedido.getDescuentoOrigen());			
			venta.agregarPartida(vdet);
			
		}
		venta.setImporte(pedido.getSubTotal());
		venta.setImpuesto(pedido.getImpuesto());
		venta.setTotal(pedido.getTotal());
		if(venta.getTipoDeVenta().equals(Venta.TipoDeVenta.CAFE))
			venta.setOrigen(OrigenDeOperacion.MOS);
		else
			venta.setOrigen(OrigenDeOperacion.CAM);
		return persistir(venta);
	}
	
	@Transactional(propagation=Propagation.MANDATORY)
	private Venta persistir(Venta factura){
		int renglon=1;
		Collections.sort(factura.getPartidas(),GlazedLists.beanPropertyComparator(VentaDet.class, "ordenp"));
		for(VentaDet det:factura.getPartidas()){
			det.setRenglon(renglon);
			det.setDocumento(factura.getDocumento());
			det.setFecha(factura.getFecha());
			renglon++;
		}
		if(factura.getVendedor()==null){
			Vendedor vendedor=(Vendedor)hibernateTemplate.get(Vendedor.class, new Long(1L));
			factura.setVendedor(vendedor);
		}
		if(factura.getCobrador()==null){
			Cobrador cobrador=(Cobrador)hibernateTemplate.get(Cobrador.class, new Long(1l));
			factura.setCobrador(cobrador);
		}
		
		Venta res=(Venta)hibernateTemplate.merge(factura);
		actualizarInventario(res);
		return res;
	}
	
	@Transactional(propagation=Propagation.MANDATORY)
	private void actualizarInventario(Venta factura){
		final Date hoy=new Date();
		final Long sucursal=factura.getSucursal().getId();
		final int year=Periodo.obtenerYear(hoy);
		final int mes=Periodo.obtenerMes(hoy)+1;
		for(VentaDet det:factura.getPartidas()){
			if(!det.getProducto().isInventariable())
				continue;
			Existencia exis=existenciaDao.buscar(det.getClave(), sucursal, year, mes);
			
			if(exis==null){
				exis=existenciaDao.generar(det.getProducto(), det.getFecha(),det.getSucursal().getId());
			}
			
			if(factura.getAutorizacionSinExistencia()==null){				
				exis.setCantidad(exis.getCantidad()+det.getCantidad());
				existenciaDao.save(exis);
			}else{				
				exis.setCantidad(exis.getCantidad()+det.getCantidad());
				exis=existenciaDao.save(exis);
				logger.info("Inventario actualizado en negativo: "+exis);
			}
		}
	}
	
	

	@Transactional(propagation=Propagation.REQUIRED)
	public Factura facturar(final List<Venta> ventas){
		Assert.notEmpty(ventas);	
		for(Venta vv:ventas){
			Assert.isNull(vv.getFactura(),"Venta ya facturada: "+vv.getDocumento());
		}
		final Venta v=ventas.get(0);
		//Filtra para ventas del mismo cliente
		CollectionUtils.filter(ventas, new BeanPropertyValueEqualsPredicate("cliente", v.getCliente()));
		
		//String serie="FAC_"+v.getTipoDeVenta().name();
		String serie;
		switch (v.getTipoDeVenta()) {
		case BEAT:
			serie="B";
			break;
		case ENERGIA:
			serie="A";
			break;
		case CAFE:
			serie="C";
			break;
		default:
			throw new RuntimeException("Serie incorrecta");
		}		
		//Folio folio=folioDao.buscarNextFolio(ventas.get(0).getSucursal(), serie);
		FolioFiscal folio=folioDao.buscarFolio(ventas.get(0).getSucursal(), serie);
		Long docto=folio.next();
		folio=folioDao.save(folio);
		Factura fac=new Factura();
		fac.setSucursal(v.getSucursal());
		fac.setTipoDeVenta(v.getTipoDeVenta());
		fac.setCliente(v.getCliente());
		fac.setRfc(v.getCliente().getRfc());
		if(fac.getRfc()==null){
			fac.setRfc("XAXX010101000");
			fac.setDesglosarIva(false);
		}
		fac.setSerie(serie);
		fac.setFolio(docto.toString());
		fac.setAnoAprobacion(folio.getAnoAprobacion());
		fac.setNoAprobacion(folio.getNoAprobacion());
		fac.setAsignacion(folio.getAsignacion());
		fac.setFoliosAsignados(folio.getFoliosAsignados());
		fac.setNombre(v.getNombre());
		fac.setTipo(v.getTipoDeVenta().name());
		Direccion d=new Direccion();
		BeanUtils.copyProperties(v.getCliente().getDireccionFiscal(), d);
		fac.setDireccionFiscal(d);
		fac.setFecha(v.getFecha());
		for(Venta source:ventas){
			fac.getVentas().add(source);
			source.setFactura(fac);
			source.setDocumento(Long.valueOf(fac.getFolio()));
		}
		fac.actualizar();
		fac =(Factura)hibernateTemplate.merge(fac);
		cfdiManager.generarCFDI(fac,false);
		return fac;
	}
	
	@Transactional(propagation=Propagation.REQUIRED)
	public Factura facturarVentaGeneral(final TipoDeVenta tipoDeVenta,Sucursal sucursal,final Date fecha){
		logger.info("Generando factura general del dia para ventas: "+tipoDeVenta+ "fecha: "+fecha);
		String hql="from Venta v where v.tipoDeVenta=@TIPO and v.fecha=? and v.factura=null";
		hql=hql.replaceAll("@TIPO", "\'"+tipoDeVenta.name()+"\'");
		List<Venta> ventas=this.hibernateTemplate.find(hql,new Object[]{fecha});
		if(ventas.isEmpty())
			return null;
		
		//final String serie="FAC_"+tipoDeVenta.name();
		String serie;
		switch (tipoDeVenta) {
		case BEAT:
			serie="B";
			break;
		case ENERGIA:
			serie="A";
			break;
		case CAFE:
			serie="C";
			break;
		default:
			throw new RuntimeException("Serie incorrecta");
		}		
		
		FolioFiscal folio=folioDao.buscarFolio(sucursal, serie);
		final Long docto=folio.next();
		
		
		final Cliente mostrador=this.clienteManager.buscarPorClave("1");
		Assert.notNull(mostrador,"No esta dado de alta el cliente mostrador");
		
		Factura fac=new Factura();
		fac.setSucursal(sucursal);
		fac.setCliente(mostrador);
		fac.setSerie(serie);
		fac.setAnoAprobacion(folio.getAnoAprobacion());
		fac.setNoAprobacion(folio.getNoAprobacion());
		fac.setTipoDeVenta(tipoDeVenta);
		fac.setFolio(docto.toString());
		fac.setAsignacion(folio.getAsignacion());
		fac.setFoliosAsignados(folio.getFoliosAsignados());
		fac.setNombre("VENTA PUBLICO EN GENERAL");
		fac.setTipo(TIPO_VENTA_GENERAL_DIARIA);
		fac.setRfc("XAXX010101000");
		Direccion d=new Direccion();
		BeanUtils.copyProperties(mostrador.getDireccionFiscal(), d);
		fac.setDireccionFiscal(d);
		fac.setFecha(fecha);
		for(Venta v:ventas){
			fac.getVentas().add(v);
			v.setFactura(fac);
		}
		fac.actualizar();
		folio=folioDao.save(folio);
		fac=(Factura)this.hibernateTemplate.merge(fac);
		cfdiManager.generarCFDI(fac,true);
		return fac;
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public Factura getFactura(Long id) {
		String hql="from Factura f " +
				" left join fetch f.ventas v where f.id=?";
		List<Factura> res=this.hibernateTemplate.find(hql,id);
		if(!res.isEmpty()){
			Factura fac=res.get(0);
			this.hibernateTemplate.initialize(fac.getCliente().getTelefonos());
			for(Venta v:fac.getVentas()){
				hibernateTemplate.initialize(v.getPartidas());
			}			
			return fac;
		}
		return null;
	}
	
	

}
